﻿using System;
#if SL4BETA
using System.Windows.Interop;
#else
using System.Windows.Interop;
using System.Runtime.InteropServices.Automation;
#endif
using System.Windows;
using Silverlight.Mainline.InjectionHelper.IO;

namespace Silverlight.Mainline.InjectionHelper
{
    public abstract class MainlineInjectorBase
    {
        private const int TEMP_SPECIAL_FOLDER_ID = 0x2;
        private const string NATIVE_INJECTION_DLL_NAME = "Silverlight.Mainline.Injector.dll";
        private const string NATIVE_DLL_METHOD_NAME = "InjectDll";
        private string m_payloadExtrationPath;

        protected MainlineInjectorBase()
        { 
            PayloadExtractionPath = GetSpecialFolder(TEMP_SPECIAL_FOLDER_ID); 
        }

        public string PayloadExtractionPath
        {
            get
            {
                return m_payloadExtrationPath;
            }
            set
            {
                m_payloadExtrationPath = value;
                if(!string.IsNullOrEmpty(m_payloadExtrationPath) && m_payloadExtrationPath.Length > 1)
                    if (m_payloadExtrationPath[m_payloadExtrationPath.Length - 1] == '\\')
                        m_payloadExtrationPath = m_payloadExtrationPath.Substring(0, m_payloadExtrationPath.Length - 1);
            }
        }

        protected abstract Uri[] GetPayloadResourcePaths();

        private void ExtractPayloadResources()
        {
            var paths = GetPayloadResourcePaths();

            byte[] buffer = new byte[2048];

            foreach (var pathUri in paths)
            {
                int lastSlashPos = pathUri.OriginalString.LastIndexOf('/');
                
                if(lastSlashPos < 0)
                    throw new InvalidOperationException(string.Format("Could not find a valid filename in: {0}", pathUri.OriginalString));

                lastSlashPos++;

                if(lastSlashPos == pathUri.OriginalString.Length -1)
                    throw new InvalidOperationException(string.Format("Could not find a valid filename in: {0}", pathUri.OriginalString));

                string filename = pathUri.OriginalString.Substring(lastSlashPos, pathUri.OriginalString.Length - lastSlashPos);

                string fullpath = string.Format(@"{0}\{1}", PayloadExtractionPath, filename);

                AdoFileStream fileStream = File.Open(fullpath, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write);

                var streamResourceInfo = Application.GetResourceStream(pathUri);

                int bytesRead = buffer.Length;

                while(true)
                {
                    bytesRead = streamResourceInfo.Stream.Read(buffer, 0, buffer.Length);

                    if (bytesRead == 0)
                        break;

                    fileStream.Write(buffer, 0, bytesRead);
                }

                fileStream.Flush();
                fileStream.Close();
            }
        }

        private string GetSpecialFolder(int folder)
        {
#if SL4BETA
            dynamic fs = ComAutomationFactory.CreateObject("Scripting.FileSystemObject");
#else
            dynamic fs = AutomationFactory.CreateObject("Scripting.FileSystemObject");
#endif
            
            return fs.GetSpecialFolder(folder).Path;
        }

        public void StartInject()
        {
            ExtractPayloadResources();
#if SL4BETA
            dynamic wshell = ComAutomationFactory.CreateObject("WScript.Shell");
#else
            dynamic wshell = AutomationFactory.CreateObject("WScript.Shell");
#endif

            var executeCommand = string.Format("rundll32 \"{0}\\{1}\",{2}", 
                                               PayloadExtractionPath, 
                                               NATIVE_INJECTION_DLL_NAME, 
                                               NATIVE_DLL_METHOD_NAME);
            
            wshell.Run(executeCommand, 0, true);
        }
    }
}
